﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace FasterWPF.Selectors
{
    public static class Select
    {
        public static DependencyObject GetParentViaVisualTree(DependencyObject obj)
        {
            if (obj == null)
                return null;

            ContentElement ce = obj as ContentElement;
            if (ce != null)
            {
                DependencyObject parent = ContentOperations.GetParent(ce);
                if (parent != null)
                    return parent;

                FrameworkContentElement fce = ce as FrameworkContentElement;
                return fce != null ? fce.Parent : null;
            }

            return VisualTreeHelper.GetParent(obj);
        }

        #region SelectorClass Methods

        #region Recurse Up

        #region With Collecting AggregateChildren

        public static FrameworkElement RecurseUpParentFrameworkElements(bool displayTypesInMessageBoxes,  int numParentsToGoUp, List<FrameworkElement> aggregateChildren, FrameworkElement TN)
        {
            FrameworkElement fe = TN;
         
            if (TN != null)
            {

                IterHelpers.ApplyLoop(numParentsToGoUp, i =>
                    {
                        if (fe != null)
                        {
                            if (Selectors.Select.GetParentViaVisualTree(fe) != null)
                            {
                                if (displayTypesInMessageBoxes)
                                {
                                    System.Windows.MessageBox.Show("V: " + Selectors.Select.GetParentViaVisualTree(fe).GetType().Name);
                                }

                                if (Selectors.Select.GetParentViaVisualTree(fe) is FrameworkElement)
                                {
                                    aggregateChildren.Add(Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement);
                                }

                                fe = Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement;
                            }
                            else if (LogicalTreeHelper.GetParent(fe) != null)
                            {
                                if (displayTypesInMessageBoxes)
                                {
                                    System.Windows.MessageBox.Show("L: " + LogicalTreeHelper.GetParent(fe).GetType().Name);
                                }

                                if (LogicalTreeHelper.GetParent(fe) is FrameworkElement)
                                {
                                    aggregateChildren.Add(LogicalTreeHelper.GetParent(fe) as FrameworkElement);
                                }

                                fe = LogicalTreeHelper.GetParent(fe) as FrameworkElement;
                            }
                        }
                    });
            }
            return fe;
        }

        public static FrameworkElement RecurseUpParentFrameworkElementsUntilType(bool displayTypesInMessageBoxes, int numParentsToGoUp, List<FrameworkElement> aggregateChildren, FrameworkElement TN, string nameOfTypeToStopAt)
        {
            FrameworkElement fe = TN;

            if (TN != null)
            {

                IterHelpers.ApplyLoop(numParentsToGoUp, i =>
                {
                    if (fe != null)
                    {
                        if (Selectors.Select.GetParentViaVisualTree(fe) != null && (fe.GetType().Name.ToString() != nameOfTypeToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("V: " + Selectors.Select.GetParentViaVisualTree(fe).GetType().Name);
                            }

                            if (Selectors.Select.GetParentViaVisualTree(fe) is FrameworkElement)
                            {
                                aggregateChildren.Add(Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement);
                            }

                            fe = Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement;
                        }
                        else if (LogicalTreeHelper.GetParent(fe) != null && (fe.GetType().Name.ToString() != nameOfTypeToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("L: " + LogicalTreeHelper.GetParent(fe).GetType().Name);
                            }

                            if (LogicalTreeHelper.GetParent(fe) is FrameworkElement)
                            {
                                aggregateChildren.Add(LogicalTreeHelper.GetParent(fe) as FrameworkElement);
                            }

                            fe = LogicalTreeHelper.GetParent(fe) as FrameworkElement;
                        }
                    }
                });
            }

            if (fe.GetType().Name.ToString() != nameOfTypeToStopAt)
            {
                throw new Exception("Unable to locate a Parent of designated Type!");
            }

            return fe;
        }

        public static FrameworkElement RecurseUpParentFrameworkElementsUntilName(bool displayTypesInMessageBoxes, int numParentsToGoUp, List<FrameworkElement> aggregateChildren, FrameworkElement TN, string nameToStopAt)
        {
            FrameworkElement fe = TN;

            if (TN != null)
            {

                IterHelpers.ApplyLoop(numParentsToGoUp, i =>
                {
                    if (fe != null)
                    {
                        if (Selectors.Select.GetParentViaVisualTree(fe) != null && (fe.Name.ToString() != nameToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("V: " + Selectors.Select.GetParentViaVisualTree(fe).GetType().Name);
                            }

                            if (Selectors.Select.GetParentViaVisualTree(fe) is FrameworkElement)
                            {
                                aggregateChildren.Add(Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement);
                            }

                            fe = Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement;
                        }
                        else if (LogicalTreeHelper.GetParent(fe) != null && (fe.Name.ToString() != nameToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("L: " + LogicalTreeHelper.GetParent(fe).GetType().Name);
                            }

                            if (LogicalTreeHelper.GetParent(fe) is FrameworkElement)
                            {
                                aggregateChildren.Add(LogicalTreeHelper.GetParent(fe) as FrameworkElement);
                            }

                            fe = LogicalTreeHelper.GetParent(fe) as FrameworkElement;
                        }
                    }
                });
            }

            if (fe.Name.ToString() != nameToStopAt)
            {
                throw new Exception("Unable to locate a Parent with that Name!");
            }

            return fe;
        }

        public static FrameworkElement RecurseUpParentFrameworkElementsUntilClass(bool displayTypesInMessageBoxes, int numParentsToGoUp, List<FrameworkElement> aggregateChildren, FrameworkElement TN, string classToStopAt)
        {
            FrameworkElement fe = TN;

            if (TN != null)
            {

                IterHelpers.ApplyLoop(numParentsToGoUp, i =>
                {
                    if (fe != null)
                    {
                        if (Selectors.Select.GetParentViaVisualTree(fe) != null && !IsInSelectorClassProperties(fe, classToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("V: " + Selectors.Select.GetParentViaVisualTree(fe).GetType().Name);
                            }

                            if (Selectors.Select.GetParentViaVisualTree(fe) is FrameworkElement)
                            {
                                aggregateChildren.Add(Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement);
                            }

                            fe = Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement;
                        }
                        else if (LogicalTreeHelper.GetParent(fe) != null && !IsInSelectorClassProperties(fe, classToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("L: " + LogicalTreeHelper.GetParent(fe).GetType().Name);
                            }

                            if (LogicalTreeHelper.GetParent(fe) is FrameworkElement)
                            {
                                aggregateChildren.Add(LogicalTreeHelper.GetParent(fe) as FrameworkElement);
                            }

                            fe = LogicalTreeHelper.GetParent(fe) as FrameworkElement;
                        }
                    }
                });
            }

            if (!IsInSelectorClassProperties(fe, classToStopAt))
            {
                throw new Exception("Unable to locate a Parent with that Selector Class Name!");
            }

            return fe;
        }
        #endregion

        #region Without Collecting AggregateChildren

        /// <summary>
        /// Recurse Up All UI Elements and Return the Last One. Traverse Up Either the Visual Tree or the Logical Tree as Needed.
        /// </summary>
        /// <param name="displayTypesInMessageBoxes"></param>
        /// <param name="numParentsToGoUp"></param>
        /// <param name="TN"></param>
        /// <returns></returns>
        public static FrameworkElement RecurseUpParentFrameworkElements(bool displayTypesInMessageBoxes, int numParentsToGoUp, FrameworkElement TN)
        {
            FrameworkElement fe = TN;

            if (TN != null)
            {
                IterHelpers.ApplyLoop(numParentsToGoUp, i =>
                {
                    if (fe != null)
                    {

                        if (Selectors.Select.GetParentViaVisualTree(fe) != null)
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("V: " + Selectors.Select.GetParentViaVisualTree(fe).GetType().Name);
                            }

                            fe = Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement;
                        }
                        else if (LogicalTreeHelper.GetParent(fe) != null)
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("L: " + LogicalTreeHelper.GetParent(fe).GetType().Name);
                            }

                            fe = LogicalTreeHelper.GetParent(fe) as FrameworkElement;
                        }
                    }


                });
            }
            return fe;
        }

        public static DependencyObject RecurseUpParentFrameworkElementsDepObj(bool displayTypesInMessageBoxes, int numParentsToGoUp, DependencyObject TN)
        {
            DependencyObject fe = TN;

            if (TN != null)
            {
                IterHelpers.ApplyLoop(numParentsToGoUp, i =>
                {
                    if (fe != null)
                    {
                        if (Selectors.Select.GetParentViaVisualTree(fe) != null)
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("V: " + Selectors.Select.GetParentViaVisualTree(fe).GetType().Name);
                            }

                            fe = Selectors.Select.GetParentViaVisualTree(fe) as DependencyObject;
                        }
                        else if (LogicalTreeHelper.GetParent(fe) != null)
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("L: " + LogicalTreeHelper.GetParent(fe).GetType().Name);
                            }

                            fe = LogicalTreeHelper.GetParent(fe) as DependencyObject;
                        }
                    }
                });
            }
            return fe;
        }

        public static FrameworkElement RecurseUpParentFrameworkElementsUntilType(bool displayTypesInMessageBoxes, int numParentsToGoUp, FrameworkElement TN, string nameOfTypeToStopAt)
        {
            FrameworkElement fe = TN;

            if (TN != null)
            {

                IterHelpers.ApplyLoop(numParentsToGoUp, i =>
                {
                    if (fe != null)
                    {
                        if (Selectors.Select.GetParentViaVisualTree(fe) != null && (fe.GetType().Name.ToString() != nameOfTypeToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("V: " + Selectors.Select.GetParentViaVisualTree(fe).GetType().Name);
                            }

                            fe = Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement;
                        }
                        else if (LogicalTreeHelper.GetParent(fe) != null && (fe.GetType().Name.ToString() != nameOfTypeToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("L: " + LogicalTreeHelper.GetParent(fe).GetType().Name);
                            }

                            fe = LogicalTreeHelper.GetParent(fe) as FrameworkElement;
                        }
                    }
                });
            }

            if (fe.GetType().Name.ToString() != nameOfTypeToStopAt)
            {
                throw new Exception("Unable to locate a Parent of designated Type!");
            }

            return fe;
        }

        public static FrameworkElement RecurseUpParentFrameworkElementsUntilName(bool displayTypesInMessageBoxes, int numParentsToGoUp, FrameworkElement TN, string nameToStopAt)
        {
            FrameworkElement fe = TN;

            if (TN != null)
            {

                IterHelpers.ApplyLoop(numParentsToGoUp, i =>
                {
                    if (fe != null)
                    {
                        if (Selectors.Select.GetParentViaVisualTree(fe) != null && (fe.Name.ToString() != nameToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("V: " + Selectors.Select.GetParentViaVisualTree(fe).GetType().Name);
                            }

                            fe = Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement;
                        }
                        else if (LogicalTreeHelper.GetParent(fe) != null && (fe.Name.ToString() != nameToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("L: " + LogicalTreeHelper.GetParent(fe).GetType().Name);
                            }

                            fe = LogicalTreeHelper.GetParent(fe) as FrameworkElement;
                        }
                    }
                });
            }

            if (fe.Name.ToString() != nameToStopAt)
            {
                throw new Exception("Unable to locate a Parent with that Name!");
            }

            return fe;
        }

        public static FrameworkElement RecurseUpParentFrameworkElementsUntilClass(bool displayTypesInMessageBoxes, int numParentsToGoUp, FrameworkElement TN, string classToStopAt)
        {
            FrameworkElement fe = TN;

            if (TN != null)
            {

                IterHelpers.ApplyLoop(numParentsToGoUp, i =>
                {
                    if (fe != null)
                    {
                        if (Selectors.Select.GetParentViaVisualTree(fe) != null && !IsInSelectorClassProperties(fe, classToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("V: " + Selectors.Select.GetParentViaVisualTree(fe).GetType().Name);
                            }

                            fe = Selectors.Select.GetParentViaVisualTree(fe) as FrameworkElement;
                        }
                        else if (LogicalTreeHelper.GetParent(fe) != null && !IsInSelectorClassProperties(fe, classToStopAt))
                        {
                            if (displayTypesInMessageBoxes)
                            {
                                System.Windows.MessageBox.Show("L: " + LogicalTreeHelper.GetParent(fe).GetType().Name);
                            }

                            fe = LogicalTreeHelper.GetParent(fe) as FrameworkElement;
                        }
                    }
                });
            }

            if (!IsInSelectorClassProperties(fe, classToStopAt))
            {
                throw new Exception("Unable to locate a Parent with that Selector Class Name!");
            }

            return fe;
        }

        #endregion

        #endregion

        public static bool IsInSelectorClassProperties(FrameworkElement fe, string selectorClassName)
        {
            return (fe.GetValue(SelectorClassSetting.SelectorClass1Property) == selectorClassName || fe.GetValue(SelectorClassSetting.SelectorClass2Property) == selectorClassName || fe.GetValue(SelectorClassSetting.SelectorClass3Property) == selectorClassName);
        }

        public static bool IsInSelectorClassProperties(FrameworkElement fe, string selectorClassName1, string selectorClassName2)
        {
            return (((fe.GetValue(SelectorClassSetting.SelectorClass1Property) == selectorClassName1 || fe.GetValue(SelectorClassSetting.SelectorClass2Property) == selectorClassName1 || fe.GetValue(SelectorClassSetting.SelectorClass3Property) == selectorClassName1))
                    && ((fe.GetValue(SelectorClassSetting.SelectorClass1Property) == selectorClassName2 || fe.GetValue(SelectorClassSetting.SelectorClass2Property) == selectorClassName2 || fe.GetValue(SelectorClassSetting.SelectorClass3Property) == selectorClassName2)));
        }

        public static bool IsInSelectorClassProperties(FrameworkElement fe, string selectorClassName1, string selectorClassName2, string selectorClassName3)
        {
            return (((fe.GetValue(SelectorClassSetting.SelectorClass1Property) == selectorClassName1 || fe.GetValue(SelectorClassSetting.SelectorClass2Property) == selectorClassName1 || fe.GetValue(SelectorClassSetting.SelectorClass3Property) == selectorClassName1))
                    && ((fe.GetValue(SelectorClassSetting.SelectorClass1Property) == selectorClassName2 || fe.GetValue(SelectorClassSetting.SelectorClass2Property) == selectorClassName2 || fe.GetValue(SelectorClassSetting.SelectorClass3Property) == selectorClassName2))
                    && ((fe.GetValue(SelectorClassSetting.SelectorClass1Property) == selectorClassName3 || fe.GetValue(SelectorClassSetting.SelectorClass2Property) == selectorClassName3 || fe.GetValue(SelectorClassSetting.SelectorClass3Property) == selectorClassName3)));
        }

        public static void ActOnElementsFilteredBySelectorClass(this List<FrameworkElement> listToFilter, string selectorClassName, Action<FrameworkElement> actionToPerform)
        {
            foreach (FrameworkElement fe in listToFilter)
            {
                if (IsInSelectorClassProperties(fe, selectorClassName))
                {
                    actionToPerform(fe);
                }
            }
        }

        public static void ActOnElementsFilteredBySelectorClass(this List<FrameworkElement> listToFilter, string selectorClassName1, string selectorClassName2, Action<FrameworkElement> actionToPerform)
        {
            foreach (FrameworkElement fe in listToFilter)
            {
                if (IsInSelectorClassProperties(fe, selectorClassName1, selectorClassName2))
                {
                    actionToPerform(fe);
                }
            }
        }

        public static void ActOnElementsFilteredBySelectorClass(this List<FrameworkElement> listToFilter, string selectorClassName1, string selectorClassName2, string selectorClassName3, Action<FrameworkElement> actionToPerform)
        {
            foreach (FrameworkElement fe in listToFilter)
            {
                if (IsInSelectorClassProperties(fe, selectorClassName1, selectorClassName2, selectorClassName3))
                {
                    actionToPerform(fe);
                }
            }
        }

        public static List<FrameworkElement> FilterElementsBySelectorClass(this List<FrameworkElement> listToFilter, string selectorClassName)
        {
            List<FrameworkElement> tempResult = new List<FrameworkElement>();

            foreach (FrameworkElement fe in listToFilter)
            {
                if (IsInSelectorClassProperties(fe, selectorClassName))
                {
                    tempResult.Add(fe);
                }
            }

            return tempResult;
        }

        public static List<FrameworkElement> FilterElementsBySelectorClass(this List<FrameworkElement> listToFilter, string selectorClassName1, string selectorClassName2)
        {
            List<FrameworkElement> tempResult = new List<FrameworkElement>();

            foreach (FrameworkElement fe in listToFilter)
            {
                if (IsInSelectorClassProperties(fe, selectorClassName1, selectorClassName2))
                {
                    tempResult.Add(fe);
                }
            }

            return tempResult;
        }

        public static List<FrameworkElement> FilterElementsBySelectorClass(this List<FrameworkElement> listToFilter, string selectorClassName1, string selectorClassName2, string selectorClassName3)
        {
            List<FrameworkElement> tempResult = new List<FrameworkElement>();

            foreach (FrameworkElement fe in listToFilter)
            {
                if (IsInSelectorClassProperties(fe, selectorClassName1, selectorClassName2, selectorClassName3))
                {
                    tempResult.Add(fe);
                }
            }

            return tempResult;
        }

       /// <summary>
       /// Recurse Down Thru All Parents Within a Root Parent and Act on All FrameworkElements filtered by Selector Class
       /// </summary>
       /// <param name="root"></param>
       /// <param name="displayTypesInMessageBoxes"></param>
       /// <returns></returns>
        public static void ActOnAllFrameworkElementsByClass(this FrameworkElement root, string selectorClassName, bool displayTypesInMessageBoxes, Action<FrameworkElement> actionToPerform)
        {
           foreach(FrameworkElement fe in SelectAllFrameworkElementsByClass(root, selectorClassName, displayTypesInMessageBoxes))
           {
               actionToPerform(fe);   
           }
        }

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Act on All FrameworkElements filtered by Selector Class
        /// </summary>
        /// <param name="root"></param>
        /// <param name="displayTypesInMessageBoxes"></param>
        /// <returns></returns>
        public static void ActOnAllFrameworkElementsByClass(this FrameworkElement root, string selectorClassName1, string selectorClassName2, bool displayTypesInMessageBoxes, Action<FrameworkElement> actionToPerform)
        {
            foreach (FrameworkElement fe in SelectAllFrameworkElementsByClass(root, selectorClassName1, selectorClassName2, displayTypesInMessageBoxes))
            {
                actionToPerform(fe);
            }
        }

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Act on All FrameworkElements filtered by Selector Class
        /// </summary>
        /// <param name="root"></param>
        /// <param name="displayTypesInMessageBoxes"></param>
        /// <returns></returns>
        public static void ActOnAllFrameworkElementsByClass(this FrameworkElement root, string selectorClassName1, string selectorClassName2, string selectorClassName3, bool displayTypesInMessageBoxes, Action<FrameworkElement> actionToPerform)
        {
            foreach (FrameworkElement fe in SelectAllFrameworkElementsByClass(root, selectorClassName1, selectorClassName2, selectorClassName3, displayTypesInMessageBoxes))
            {
                actionToPerform(fe);
            }
        }
        
       /// <summary>
       /// Recurse Down Thru All Parents Within a Root Parent and Select All FrameworkElements filtered by Selector Class
       /// </summary>
       /// <param name="root"></param>
       /// <param name="displayTypesInMessageBoxes"></param>
       /// <returns></returns>
       public static List<FrameworkElement> SelectAllFrameworkElementsByClass(this FrameworkElement root, string selectorClassName, bool displayTypesInMessageBoxes)
       {
            List<FrameworkElement> aggregateChildren = new List<FrameworkElement>();
            List<FrameworkElement> topLevelChildren = new List<FrameworkElement>();

            if (IsInSelectorClassProperties(root, selectorClassName))
            {
                aggregateChildren.Add(root);
            }

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                Dictionary<string, object> windowChildren = root.GetChildLookupDictionary<FrameworkElement>();
                foreach (FrameworkElement frameElem in windowChildren.Values)
                {
                    //if this is truly a parent
                    if (isTrulyAParent(frameElem))
                    {
                        topLevelChildren.Add(frameElem);
                    }
                    else
                    {
                        if (IsInSelectorClassProperties(frameElem, selectorClassName))
                        {
                           aggregateChildren.Add(frameElem);
                        }
                    }
                }
            }
            else
            {
                throw new NotAParentException("Root parameter must be an initialized Parent!");
            }

            recurseFrameworkElementsByClass(displayTypesInMessageBoxes, selectorClassName, null, null, aggregateChildren, topLevelChildren);
            return aggregateChildren;
        }

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Select All FrameworkElements filtered by Selector Class
        /// </summary>
        /// <param name="root"></param>
        /// <param name="displayTypesInMessageBoxes"></param>
        /// <returns></returns>
        public static List<FrameworkElement> SelectAllFrameworkElementsByClass(this FrameworkElement root, string selectorClassName1, string selectorClassName2, bool displayTypesInMessageBoxes)
        {
            List<FrameworkElement> aggregateChildren = new List<FrameworkElement>();
            List<FrameworkElement> topLevelChildren = new List<FrameworkElement>();

            if (IsInSelectorClassProperties(root, selectorClassName1, selectorClassName2))
            {
                aggregateChildren.Add(root);
            }

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                Dictionary<string, object> windowChildren = root.GetChildLookupDictionary<FrameworkElement>();
                foreach (FrameworkElement frameElem in windowChildren.Values)
                {
                    //if this is truly a parent
                    if (isTrulyAParent(frameElem))
                    {
                        topLevelChildren.Add(frameElem);
                    }
                    else
                    {
                        if (IsInSelectorClassProperties(frameElem, selectorClassName1, selectorClassName2))
                        {
                            aggregateChildren.Add(frameElem);
                        }
                    }
                }
            }
            else
            {
                throw new NotAParentException("Root parameter must be an initialized Parent!");
            }

            recurseFrameworkElementsByClass(displayTypesInMessageBoxes, selectorClassName1, selectorClassName2, null, aggregateChildren, topLevelChildren);
            return aggregateChildren;
        }

        public static bool IsThisElementAParent(this FrameworkElement root)
        {
            return ((root != null) && (root.Tag != null) && (root.Tag is Dictionary<string, object>) && root.GetChildLookupDictionary<FrameworkElement>().Count > 0 && !(root is TextBoxBase));
        }

        private static bool isTrulyAParent(FrameworkElement root)
        {
            return ((root != null) && (root.Tag != null) && (root.Tag is Dictionary<string, object>) && root.GetChildLookupDictionary<FrameworkElement>().Count>0 && !(root is TextBoxBase));
        }

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Select All FrameworkElements filtered by Selector Class
        /// </summary>
        /// <param name="root"></param>
        /// <param name="displayTypesInMessageBoxes"></param>
        /// <returns></returns>
        public static List<FrameworkElement> SelectAllFrameworkElementsByClass(this FrameworkElement root, string selectorClassName1, string selectorClassName2, string selectorClassName3, bool displayTypesInMessageBoxes)
        {
            List<FrameworkElement> aggregateChildren = new List<FrameworkElement>();
            List<FrameworkElement> topLevelChildren = new List<FrameworkElement>();

            if (IsInSelectorClassProperties(root, selectorClassName1, selectorClassName2, selectorClassName3))
            {
                aggregateChildren.Add(root);
            }

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                Dictionary<string, object> windowChildren = root.GetChildLookupDictionary<FrameworkElement>();
                foreach (FrameworkElement frameElem in windowChildren.Values)
                {
                    //if this is truly a parent
                    if (isTrulyAParent(frameElem))
                    {
                        topLevelChildren.Add(frameElem);
                    }
                    else
                    {
                        if (IsInSelectorClassProperties(frameElem, selectorClassName1, selectorClassName2, selectorClassName3))
                        {
                            aggregateChildren.Add(frameElem);
                        }
                    }
                }
            }
            else
            {
                throw new NotAParentException("Root parameter must be an initialized Parent!");
            }

            recurseFrameworkElementsByClass(displayTypesInMessageBoxes, selectorClassName1, selectorClassName2, selectorClassName3, aggregateChildren, topLevelChildren);
            return aggregateChildren;
        }

        private static void recurseFrameworkElementsByClass(bool displayTypesInMessageBoxes, string selectorClassName1, string selectorClassName2, string selectorClassName3, List<FrameworkElement> aggregateChildren, List<FrameworkElement> topLevelChildren)
        {
           RecursionHelper<FrameworkElement> recurser = new RecursionHelper<FrameworkElement>();

           foreach (FrameworkElement TN in topLevelChildren)
           {
               if (TN != null)
               {
                   recurser.RecurseTree(TN, a =>
                   {
                       if (displayTypesInMessageBoxes)
                       {
                           System.Windows.MessageBox.Show("P: " + a.GetType().Name);
                       }

                       if (a is FrameworkElement)
                       {
                           if (selectorClassName3 == null && selectorClassName2 == null && IsInSelectorClassProperties(a, selectorClassName1))
                            {
                               aggregateChildren.Add(a);
                            }
                           else if (selectorClassName3 == null && IsInSelectorClassProperties(a, selectorClassName1, selectorClassName2))
                            {
                               aggregateChildren.Add(a);
                            }
                           else if (IsInSelectorClassProperties(a, selectorClassName1, selectorClassName2, selectorClassName3))
                            {
                                aggregateChildren.Add(a);
                            }
                       }
                       
                   }, b =>
                   {
                       if (displayTypesInMessageBoxes)
                       {
                           System.Windows.MessageBox.Show("C: " + b.GetType().Name);
                       }
                           
                       if (b is FrameworkElement)
                       {
                            if (selectorClassName3 == null && selectorClassName2 == null && IsInSelectorClassProperties(b, selectorClassName1))
                            {
                                aggregateChildren.Add(b);
                            }
                            else if (selectorClassName3 == null && IsInSelectorClassProperties(b, selectorClassName1, selectorClassName2))
                            {
                                aggregateChildren.Add(b);
                            }
                            else if (IsInSelectorClassProperties(b, selectorClassName1, selectorClassName2, selectorClassName3))
                            {
                                aggregateChildren.Add(b);
                            }
                       }                       
                   },
                      c =>
                      {
                          Dictionary<string, object> innerChildren = new Dictionary<string, object>();
                          int myCount = 0;
                          //if this is truly a parent
                          if (isTrulyAParent(c))
                          {
                              innerChildren = c.GetChildLookupDictionary<FrameworkElement>();
                              if (innerChildren != null)
                              {
                                  myCount = innerChildren.Count;
                              }
                          }

                          return myCount;
                      },
                     (d, i) =>
                     {

                         FrameworkElement fElem = null;
                         Dictionary<string, object> innerChildren2 = new Dictionary<string, object>();
                         int counter = 0;
                         //if this is truly a parent
                         if (isTrulyAParent(d))
                         {
                             innerChildren2 = d.GetChildLookupDictionary<FrameworkElement>();
                             if (innerChildren2 != null)
                             {
                                 foreach (object obj in innerChildren2.Values)
                                 {
                                     if (counter == i)
                                     {
                                         if (obj is FrameworkElement)
                                         {
                                             fElem = (obj as FrameworkElement);
                                         }
                                     }
                                     counter++;
                                 }
                             }
                         }

                         return fElem;
                     }
                   );
               }
           }
        }

        #endregion

        /// <summary>
        /// Retrieve All Composites from a Parent
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="chain"></param>
        /// <returns></returns>
        public static List<Border> RetrieveCollection<T>(this T chain)
            where T: FrameworkElement
        {
           IParent currentComposite = chain.GetSetting<T, IParent>("IParent");
           List<Border> listOfChildren = currentComposite.RetrieveCollection(chain);

           return listOfChildren;
        }

        #region DisposeEvents Against Parents

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Call DisposeEvents on each Parent
        /// </summary>
        /// <param name="root"></param>
        public static void DisposeEventsOnAllParents(this FrameworkElement root)
        {
            root.ActOnParentFrameworkElements(fe =>
            {                    
                //if this is truly a parent
                if (isTrulyAParent(fe))
                {
                    fe.DisposeEvents<FrameworkElement>();
                }
            });
        }

        #endregion

        #region Select a Border by Key

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Find the Composite that matches the Key
        /// </summary>
        /// <param name="root"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static Border SelectCompositeByKey(this FrameworkElement root, string key)
        {
            List<Border> newListT = new List<Border>();

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                List<Border> bItems = SelectCompositesOnly(root, false).Where((fe) => { bool tempR = false; if ((fe is Border) && ((fe as Border).Child is ContentControl) && (((fe as Border).Child as ContentControl).Tag is FrameworkElement) && (((((fe as Border).Child as ContentControl).Tag as FrameworkElement)).Tag != null) && ((fe as Border).GetKey() == key)) { tempR = true; }; return tempR; }).ToList();

                foreach (Border b in bItems)
                {
                    newListT.Add(b);
                }
            }
            else
            {
                throw new NotAParentException("Root parameter must be an initialized Parent!");
            }

            if (newListT.Count == 0)
            {
                throw new CompositeKeyNotFoundException("No composite could be found with that key in this root!", key);
            }

            return newListT[0];
        }

        /// <summary>
        ///  Recurse Down Thru All Parents Within a Root Parent and Find the Composite that matches the Key. Won't Throw an Error if None Found; Merely Returns False
        /// </summary>
        /// <param name="root"></param>
        /// <param name="key"></param>
        /// <param name="border"></param>
        /// <returns></returns>
        public static bool TrySelectCompositeByKey(this FrameworkElement root, string key, out Border border)
        {
            List<Border> newListT = new List<Border>();

            bool tempResult = false;

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                List<Border> bItems = SelectCompositesOnly(root, false).Where((fe) => { bool tempR = false; if ((fe is Border) && ((fe as Border).Child is ContentControl) && (((fe as Border).Child as ContentControl).Tag is FrameworkElement) && (((((fe as Border).Child as ContentControl).Tag as FrameworkElement)).Tag != null) && ((fe as Border).GetKey() == key)) { tempR = true; }; return tempR; }).ToList();

                foreach (Border b in bItems)
                {
                    newListT.Add(b);
                }
            }

            if (newListT.Count == 0)
            {
                border = null;
            }
            else
            {
                tempResult = true;
                border = newListT[0];                
            }

            return tempResult;
        }

        #endregion

        #region Apply Action Against Parents Only

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Perform the Action against each Parent
        /// </summary>
        /// <param name="root"></param>
        /// <param name="actionToPerform"></param>
        public static void ActOnParentFrameworkElements(this FrameworkElement root, Action<FrameworkElement> actionToPerform)
        {
            List<FrameworkElement> topLevelChildren = new List<FrameworkElement>();

            actionToPerform(root);

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                Dictionary<string, object> windowChildren = root.GetChildLookupDictionary<FrameworkElement>();
                foreach (FrameworkElement frameElem in windowChildren.Values)
                {
                    //if this is truly a parent
                    if (isTrulyAParent(frameElem))
                    {
                        topLevelChildren.Add(frameElem);
                    }
                }
            }
            else
            {
                throw new NotAParentException("Root parameter must be an initialized Parent!");
            }

            recurseActOnParentFrameworkElements(topLevelChildren, actionToPerform);
        }

        private static void recurseActOnParentFrameworkElements(List<FrameworkElement> topLevelChildren, Action<FrameworkElement> actionToPerform)
        {
            RecursionHelper<FrameworkElement> recurser = new RecursionHelper<FrameworkElement>();

            foreach (FrameworkElement TN in topLevelChildren)
            {
                if (TN != null)
                {
                    recurser.RecurseTree(TN, a =>
                    {
                        if (a is FrameworkElement)
                        {
                            actionToPerform(a);
                        }

                    }, b =>
                    {
                        // DO NOT ACT ON CHILD
                    },
                       c =>
                       {
                           Dictionary<string, object> innerChildren = new Dictionary<string, object>();
                           int myCount = 0;
                           //if this is truly a parent
                           if (isTrulyAParent(c))
                           {
                               innerChildren = c.GetChildLookupDictionary<FrameworkElement>();
                               if (innerChildren != null)
                               {
                                   myCount = innerChildren.Count;
                               }

                           }

                           return myCount;
                       },
                      (d, i) =>
                      {

                          FrameworkElement fElem = null;
                          Dictionary<string, object> innerChildren2 = new Dictionary<string, object>();
                          int counter = 0;
                          //if this is truly a parent
                          if (isTrulyAParent(d))
                          {
                              innerChildren2 = d.GetChildLookupDictionary<FrameworkElement>();
                              if (innerChildren2 != null)
                              {
                                  foreach (object obj in innerChildren2.Values)
                                  {
                                      if (counter == i)
                                      {
                                          if (obj is FrameworkElement)
                                          {
                                              fElem = (obj as FrameworkElement);
                                          }
                                      }
                                      counter++;
                                  }
                              }
                          }

                          return fElem;
                      }
                    );
                }
            }
        }


        #endregion

        #region Select Composites Only

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Select All Composites
        /// </summary>
        /// <param name="root"></param>
        /// <param name="displayTypesInMessageBoxes"></param>
        /// <returns></returns>
        public static List<Border> SelectCompositesOnly(this FrameworkElement root, bool displayTypesInMessageBoxes)
        {
            List<object> aggregateChildren = new List<object>();
            
            List<FrameworkElement> parentList = root.SelectParentFrameworkElementsOnly(false);
            foreach (FrameworkElement fe in parentList)
            {
                aggregateChildren.AddRange(fe.GetCompositeLookupDictionary().Values);
            }

            List<Border> retVal = new List<Border>();
            foreach (object o in aggregateChildren)
            { 
                if(o is Border)
                {
                    retVal.Add(o as Border);
                }
            }
            
            return retVal;
        }

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Select Composite by Name
        /// </summary>
        /// <param name="root"></param>
        /// <param name="displayTypesInMessageBoxes"></param>
        /// <returns></returns>
        public static T SelectCompositeByName<T>(this FrameworkElement root, string name)
            where T: FrameworkElement
        {
            T tempResult = default(T);

            Border borderResult = root.SelectCompositesOnly(false).Where(bdr => { return bdr.GetParentFromComposite<FrameworkElement>().Name == name; }).FirstOrDefault();

            if (borderResult != null)
            {
                tempResult = borderResult.GetParentFromComposite<T>();
            }
            
            return tempResult;
        }

        #endregion

        #region Select Parents Only or Children Only

        //Parents Only

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Select All Parents
        /// </summary>
        /// <param name="root"></param>
        /// <param name="displayTypesInMessageBoxes"></param>
        /// <returns></returns>
        public static List<FrameworkElement> SelectParentFrameworkElementsOnly(this FrameworkElement root, bool displayTypesInMessageBoxes)
        {
            List<FrameworkElement> aggregateChildren = new List<FrameworkElement>();
            List<FrameworkElement> topLevelChildren = new List<FrameworkElement>();

            aggregateChildren.Add(root);

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                Dictionary<string, object> windowChildren = root.GetChildLookupDictionary<FrameworkElement>();
                foreach (FrameworkElement frameElem in windowChildren.Values)
                {
                    //if this is truly a parent
                    if (isTrulyAParent(frameElem))
                    {
                        topLevelChildren.Add(frameElem);
                    }
                }
            }
            else
            {
                throw new NotAParentException("Root parameter must be an initialized Parent!");
            }

            recurseParentFrameworkElementsOnly(displayTypesInMessageBoxes, aggregateChildren, topLevelChildren);
            return aggregateChildren;
        }

        private static void recurseParentFrameworkElementsOnly(bool displayTypesInMessageBoxes, List<FrameworkElement> aggregateChildren, List<FrameworkElement> topLevelChildren)
        {
            RecursionHelper<FrameworkElement> recurser = new RecursionHelper<FrameworkElement>();

            foreach (FrameworkElement TN in topLevelChildren)
            {
                if (TN != null)
                {
                    recurser.RecurseTree(TN, a =>
                    {
                        if (displayTypesInMessageBoxes)
                        {
                            System.Windows.MessageBox.Show("P: " + a.GetType().Name);
                        }

                        if (a is FrameworkElement)
                        {
                            aggregateChildren.Add(a);
                        }

                    }, b =>
                    {
                        //if (displayTypesInMessageBoxes)
                        //{
                        //    System.Windows.MessageBox.Show("C: " + b.GetType().Name);
                        //}

                        //if (b is FrameworkElement)
                        //{
                            //aggregateChildren.Add(b);  DO NOT ADD CHILD
                        //}

                    },
                       c =>
                       {
                           Dictionary<string, object> innerChildren = new Dictionary<string, object>();
                           int myCount = 0;
                           //if this is truly a parent
                           if (isTrulyAParent(c))
                           {
                               innerChildren = c.GetChildLookupDictionary<FrameworkElement>();
                               if (innerChildren != null)
                               {
                                   myCount = innerChildren.Count;
                               }

                           }

                           return myCount;
                       },
                      (d, i) =>
                      {

                          FrameworkElement fElem = null;
                          Dictionary<string, object> innerChildren2 = new Dictionary<string, object>();
                          int counter = 0;
                          //if this is truly a parent
                          if (isTrulyAParent(d))
                          {
                              innerChildren2 = d.GetChildLookupDictionary<FrameworkElement>();
                              if (innerChildren2 != null)
                              {
                                  foreach (object obj in innerChildren2.Values)
                                  {
                                      if (counter == i)
                                      {
                                          if (obj is FrameworkElement)
                                          {
                                              fElem = (obj as FrameworkElement);
                                          }
                                      }
                                      counter++;
                                  }
                              }
                          }

                          return fElem;
                      }
                    );
                }
            }
        }

        //Children Only
       
        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Select All Child FrameworkElements
        /// </summary>
        /// <param name="root"></param>
        /// <param name="displayTypesInMessageBoxes"></param>
        /// <returns></returns>
        public static List<FrameworkElement> SelectChildFrameworkElementsOnly(this FrameworkElement root, bool displayTypesInMessageBoxes)
        {
            List<FrameworkElement> aggregateChildren = new List<FrameworkElement>();
            List<FrameworkElement> topLevelChildren = new List<FrameworkElement>();

            //aggregateChildren.Add(root); Do NOT Add Parent

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                Dictionary<string, object> windowChildren = root.GetChildLookupDictionary<FrameworkElement>();
                foreach (FrameworkElement frameElem in windowChildren.Values)
                {    
                    //if this is truly a parent
                    if (isTrulyAParent(frameElem))
                    {
                        topLevelChildren.Add(frameElem);
                    }
                    else
                    {
                        aggregateChildren.Add(frameElem);
                    }
                }
            }
            else
            {
                throw new NotAParentException("Root parameter must be an initialized Parent!");
            }

            recurseChildFrameworkElementsOnly(displayTypesInMessageBoxes, aggregateChildren, topLevelChildren);
            return aggregateChildren;
        }

        private static void recurseChildFrameworkElementsOnly(bool displayTypesInMessageBoxes, List<FrameworkElement> aggregateChildren, List<FrameworkElement> topLevelChildren)
        {
            RecursionHelper<FrameworkElement> recurser = new RecursionHelper<FrameworkElement>();

            foreach (FrameworkElement TN in topLevelChildren)
            {
                if (TN != null)
                {
                    recurser.RecurseTree(TN, a =>
                    {
                        //if (displayTypesInMessageBoxes)
                        //{
                         //   System.Windows.MessageBox.Show("P: " + a.GetType().Name);
                        //}

                        //if (a is FrameworkElement)
                        //{
                          //  aggregateChildren.Add(a);  DO NOT ADD PARENT
                        //}

                    }, b =>
                    {
                        if (displayTypesInMessageBoxes)
                        {
                            System.Windows.MessageBox.Show("C: " + b.GetType().Name);
                        }

                        if (b is FrameworkElement)
                        {
                            aggregateChildren.Add(b);
                        }

                    },
                       c =>
                       {
                           Dictionary<string, object> innerChildren = new Dictionary<string, object>();
                           int myCount = 0;
                           //if this is truly a parent
                           if (isTrulyAParent(c))
                           {
                               innerChildren = c.GetChildLookupDictionary<FrameworkElement>();
                               if (innerChildren != null)
                               {
                                   myCount = innerChildren.Count;
                               }

                           }

                           return myCount;
                       },
                      (d, i) =>
                      {

                          FrameworkElement fElem = null;
                          Dictionary<string, object> innerChildren2 = new Dictionary<string, object>();
                          int counter = 0;
                          //if this is truly a parent
                          if (isTrulyAParent(d))
                          {
                              innerChildren2 = d.GetChildLookupDictionary<FrameworkElement>();
                              if (innerChildren2 != null)
                              {
                                  foreach (object obj in innerChildren2.Values)
                                  {
                                      if (counter == i)
                                      {
                                          if (obj is FrameworkElement)
                                          {
                                              fElem = (obj as FrameworkElement);
                                          }
                                      }
                                      counter++;
                                  }
                              }
                          }

                          return fElem;
                      }
                    );
                }
            }
        }
        #endregion

        #region Select All and Select All By Type

        /// <summary>
        /// Recurse Down Thru All Parents Within a Root Parent and Select All FrameworkElements that match the Type T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="root"></param>
        /// <returns></returns>
        public static List<T>  SelectFrameworkElementsByType<T>(this FrameworkElement root)
            where T : FrameworkElement
        {
            List<T> newListT = new List<T>();

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                List<FrameworkElement> cItems = SelectAllFrameworkElements(root, false).Where((fe) => { bool tempR = false; if (fe is T) { tempR = true; }; return tempR; }).ToList();

                foreach (T t in cItems)
                {
                    newListT.Add(t);
                }
            }
            else
            {
                throw new NotAParentException("Root parameter must be an initialized Parent!");
            }

            return newListT;
        }

       /// <summary>
       /// Recurse Down Thru All Parents Within a Root Parent and Select All FrameworkElements
       /// </summary>
       /// <param name="root"></param>
       /// <param name="displayTypesInMessageBoxes"></param>
       /// <returns></returns>
       public static List<FrameworkElement> SelectAllFrameworkElements(this FrameworkElement root, bool displayTypesInMessageBoxes)
       {
            List<FrameworkElement> aggregateChildren = new List<FrameworkElement>();
            List<FrameworkElement> topLevelChildren = new List<FrameworkElement>();
           
            aggregateChildren.Add(root);

            //if this is truly a parent
            if (isTrulyAParent(root))
            {
                Dictionary<string, object> windowChildren = root.GetChildLookupDictionary<FrameworkElement>();
                foreach (FrameworkElement frameElem in windowChildren.Values)
                {
                    //if this is truly a parent
                    if (isTrulyAParent(frameElem))
                    {
                        topLevelChildren.Add(frameElem);
                    }
                    else
                    {
                        aggregateChildren.Add(frameElem);
                    }
                }
            }
            else
            {
                throw new NotAParentException("Root parameter must be an initialized Parent!");
            }

            recurseFrameworkElements(displayTypesInMessageBoxes, aggregateChildren, topLevelChildren);
            return aggregateChildren;
        }

       private static void recurseFrameworkElements(bool displayTypesInMessageBoxes, List<FrameworkElement> aggregateChildren, List<FrameworkElement> topLevelChildren)
       {
           RecursionHelper<FrameworkElement> recurser = new RecursionHelper<FrameworkElement>();

           foreach (FrameworkElement TN in topLevelChildren)
           {
               if (TN != null)
               {
                   recurser.RecurseTree(TN, a =>
                   {
                       if (displayTypesInMessageBoxes)
                       {
                           System.Windows.MessageBox.Show("P: " + a.GetType().Name);
                       }

                       if (a is FrameworkElement)
                       {
                           aggregateChildren.Add(a);
                       }
                       
                   }, b =>
                   {
                       if (displayTypesInMessageBoxes)
                       {
                           System.Windows.MessageBox.Show("C: " + b.GetType().Name);
                       }
                           
                       if (b is FrameworkElement)
                       {
                           aggregateChildren.Add(b);
                       }
                       
                   },
                      c =>
                      {
                          Dictionary<string, object> innerChildren = new Dictionary<string, object>();
                          int myCount = 0;
                          //if this is truly a parent
                          if (isTrulyAParent(c))
                          {
                              innerChildren = c.GetChildLookupDictionary<FrameworkElement>();
                              if (innerChildren != null)
                              {
                                  myCount = innerChildren.Count;
                              }

                          }

                          return myCount;
                      },
                     (d, i) =>
                     {

                         FrameworkElement fElem = null;
                         Dictionary<string, object> innerChildren2 = new Dictionary<string, object>();
                         int counter = 0;
                         //if this is truly a parent
                         if (isTrulyAParent(d))
                         {
                             innerChildren2 = d.GetChildLookupDictionary<FrameworkElement>();
                             if (innerChildren2 != null)
                             {
                                 foreach (object obj in innerChildren2.Values)
                                 {
                                     if (counter == i)
                                     {
                                         if (obj is FrameworkElement)
                                         {
                                             fElem = (obj as FrameworkElement);
                                         }
                                     }
                                     counter++;
                                 }
                             }
                         }

                         return fElem;
                     }
                   );
               }
           }
       }
       #endregion

        #region Select Containers From All T Composites

       /// <summary>
       /// Recurse Down Thru All Parents Within a Root Parent and Select All Containers T From Within Any Composites
       /// </summary>
       /// <param name="root"></param>
       /// <param name="displayTypesInMessageBoxes"></param>
       /// <returns></returns>
       public static List<T> SelectContainersFromAllTComposites<T>(this FrameworkElement root, ContainerType c, bool displayTypesInMessageBoxes)
           where T : FrameworkElement
      {
           List<T> aggregateChildren = new List<T>();
           List<FrameworkElement> topLevelChildren = new List<FrameworkElement>();

           //if this is truly a parent
           if (isTrulyAParent(root))
           {
               Dictionary<string, object> windowChildren = root.GetChildLookupDictionary<FrameworkElement>();
               foreach (FrameworkElement frameElem in windowChildren.Values)
               {                    
                   //if this is truly a parent
                   if (isTrulyAParent(frameElem))
                   {
                       topLevelChildren.Add(frameElem);
                   }
                   else
                   {
                       if (frameElem.GetParentFromChildElement<FrameworkElement>().GetContainerType<FrameworkElement>() == c)
                       {
                           T tempT = frameElem.GetParentFromChildElement<FrameworkElement>().GetIComposite<FrameworkElement>().GetContainer<T>(frameElem.GetBorderFromChildElement<FrameworkElement>() as Border);
                                                          
                           if (aggregateChildren.Contains(tempT))
                           {
                               //if T already in list then do nothing
                           }
                           else
                           {
                               aggregateChildren.Add(tempT);
                           }
                       }
                   }
               }
           }
           else
           {
               throw new NotAParentException("Root parameter must be an initialized Parent!");
           }

           recurseTComposites<T>(c, displayTypesInMessageBoxes, aggregateChildren, topLevelChildren);
           return aggregateChildren;
     }

       private static void recurseTComposites<T>(ContainerType c1, bool displayTypesInMessageBoxes, List<T> aggregateChildren, List<FrameworkElement> topLevelChildren)
                    where T: FrameworkElement
     {
         RecursionHelper<FrameworkElement> recurser = new RecursionHelper<FrameworkElement>();

         foreach (FrameworkElement TN in topLevelChildren)
           {
               if (TN != null)
               {
                   recurser.RecurseTree(TN, a =>
                   {
                       if (displayTypesInMessageBoxes)
                       {
                           System.Windows.MessageBox.Show("P: " + a.GetType().Name);
                       }

                       if ((a is FrameworkElement) )  //assure this is a true Composite and NOT an IParent
                       {
                           //If the framework element's parent is defined to house Grid Containers, a.k.a. GridComposites then get the Grid
                           if (a.GetParentFromChildElement<FrameworkElement>().GetContainerType<FrameworkElement>() == c1)
                           {
                               T tempT = a.GetParentFromChildElement<FrameworkElement>().GetIComposite<FrameworkElement>().GetContainer<T>(a.GetBorderFromChildElement<FrameworkElement>() as Border);
                               if (aggregateChildren.Contains(tempT))
                               {
                                   //if T already in list then do nothing
                               }
                               else
                               {
                                   aggregateChildren.Add(tempT);
                               }                               
                           }
                       }
                       
                   }, b =>
                   {
                       if (displayTypesInMessageBoxes)
                       {
                           System.Windows.MessageBox.Show("C: " + b.GetType().Name);
                       }

                       if ((b is FrameworkElement))  //assure this is a true Composite and NOT an IParent
                       {
                           if ((b.Tag == null))
                           {
                               //If the framework element's parent is defined to house Grid Containers, a.k.a. GridComposites then get the Grid
                               if (b.GetParentFromChildElement<FrameworkElement>().GetContainerType<FrameworkElement>() == c1)
                               {
                                   T tempTb = b.GetParentFromChildElement<FrameworkElement>().GetIComposite<FrameworkElement>().GetContainer<T>(b.GetBorderFromChildElement<FrameworkElement>() as Border);
                                   if (aggregateChildren.Contains(tempTb))
                                   {
                                       //if Grid already in list then do nothing
                                   }
                                   else
                                   {
                                       aggregateChildren.Add(tempTb);
                                   }            
                               }
                           }
                       }
                       
                   },
 c =>
 {
     Dictionary<string, object> innerChildren = new Dictionary<string, object>();
     int myCount = 0;
     //if this is truly a parent
     if (isTrulyAParent(c))
     {
         innerChildren = c.GetChildLookupDictionary<FrameworkElement>();
         if (innerChildren != null)
         {
             myCount = innerChildren.Count;
         }

     }

     return myCount;
 },
                     (d, i) =>
                     {
                         FrameworkElement fElem = null;
                         Dictionary<string, object> innerChildren2 = new Dictionary<string, object>();
                         int counter = 0;
                         //if this is truly a parent
                         if (isTrulyAParent(d))
                         {
                             innerChildren2 = d.GetChildLookupDictionary<FrameworkElement>();
                             if (innerChildren2 != null)
                             {
                                 foreach (object obj in innerChildren2.Values)
                                 {
                                     if (counter == i)
                                     {
                                         if (obj is FrameworkElement)
                                         {
                                             fElem = (obj as FrameworkElement);
                                         }
                                     }
                                     counter++;
                                 }
                             }
                         }

                         return fElem;
                     }
                   );
               }
           }
       } //end of RecurseGridComposites

       #endregion
 
        #region Select HeaderedContent from HeaderedContentControls

       /// <summary>
       /// Recurse Down Thru All Parents Within a Root Parent and Select All HeaderedContent from HeaderedContentControls
       /// </summary>
       /// <param name="root"></param>
       /// <param name="displayTypesInMessageBoxes"></param>
       /// <returns></returns>
       public static List<object> SelectAllHeaderedContent(this FrameworkElement root, bool displayTypesInMessageBoxes)
       {
          List<object> aggregateChildren = new List<object>();
           List<FrameworkElement> topLevelChildren = new List<FrameworkElement>();

           //if this is truly a parent
           if (isTrulyAParent(root))
           {
               Dictionary<string, object> windowChildren = root.GetChildLookupDictionary<FrameworkElement>();
               foreach (FrameworkElement frameElem in windowChildren.Values)
               {                    
                   //if this is truly a parent
                   if (isTrulyAParent(frameElem))
                   {
                       topLevelChildren.Add(frameElem);
                   }
                   else
                   {
                       if (frameElem is HeaderedContentControl)
                       {
                           object headeredContent = (frameElem as HeaderedContentControl).Header;

                           if (aggregateChildren.Contains(headeredContent))
                           {
                               //if T already in list then do nothing
                           }
                           else
                           {
                               aggregateChildren.Add(headeredContent);
                           }
                       }
                   }
               }
           }
           else
           {
               throw new NotAParentException("Root parameter must be an initialized Parent!");
           }

           recurseHeaderedContent(displayTypesInMessageBoxes, aggregateChildren, topLevelChildren);
           return aggregateChildren;
     }

       private static void recurseHeaderedContent(bool displayTypesInMessageBoxes, List<object> aggregateChildren, List<FrameworkElement> topLevelChildren)
     {
         RecursionHelper<FrameworkElement> recurser = new RecursionHelper<FrameworkElement>();

         foreach (FrameworkElement TN in topLevelChildren)
           {
               if (TN != null)
               {
                   recurser.RecurseTree(TN, a =>
                   {
                       if (displayTypesInMessageBoxes)
                       {
                           System.Windows.MessageBox.Show("P: " + a.GetType().Name);
                       }

                       if ((a is FrameworkElement) )  //assure this is a true Composite and NOT an IParent
                       {
                           if (a is HeaderedContentControl)
                           {
                               object headeredContent = (a as HeaderedContentControl).Header;

                               if (aggregateChildren.Contains(headeredContent))
                               {
                                   //if T already in list then do nothing
                               }
                               else
                               {
                                   aggregateChildren.Add(headeredContent);
                               }
                           }
                       }
                       
                   }, b =>
                   {
                       if (displayTypesInMessageBoxes)
                       {
                           System.Windows.MessageBox.Show("C: " + b.GetType().Name);
                       }

                       if ((b is FrameworkElement))  //assure this is a true Composite and NOT an IParent
                       {
                           if ((b.Tag == null))
                           {
                               if (b is HeaderedContentControl)
                               {
                                   object headeredContentb = (b as HeaderedContentControl).Header;

                                   if (aggregateChildren.Contains(headeredContentb))
                                   {
                                       //if T already in list then do nothing
                                   }
                                   else
                                   {
                                       aggregateChildren.Add(headeredContentb);
                                   }
                               }
                           }
                       }
                       
                   },
 c =>
 {
     Dictionary<string, object> innerChildren = new Dictionary<string, object>();
     int myCount = 0;
     //if this is truly a parent
     if (isTrulyAParent(c))
     {
         innerChildren = c.GetChildLookupDictionary<FrameworkElement>();
         if (innerChildren != null)
         {
             myCount = innerChildren.Count;
         }

     }

     return myCount;
 },
                     (d, i) =>
                     {
                         FrameworkElement fElem = null;
                         Dictionary<string, object> innerChildren2 = new Dictionary<string, object>();
                         int counter = 0;
                         //if this is truly a parent
                         if (isTrulyAParent(d))
                         {
                             innerChildren2 = d.GetChildLookupDictionary<FrameworkElement>();
                             if (innerChildren2 != null)
                             {
                                 foreach (object obj in innerChildren2.Values)
                                 {
                                     if (counter == i)
                                     {
                                         if (obj is FrameworkElement)
                                         {
                                             fElem = (obj as FrameworkElement);
                                         }
                                     }
                                     counter++;
                                 }
                             }
                         }

                         return fElem;
                     }
                   );
               }
           }
       } //end of RecurseHeaderedContentControls
       #endregion

    }//end of Selectors class
   

   #region Recursion Helper

    public class RecursionHelper<T>
    {
        public RecursionHelper() { }

        private int pnc = -1;  //Parent Node Counter
        private List<T> parents = null;    //Parent Nodes in Row
        private List<int> counters = null;  //Child Counters for each Parent Node in Row
        private T currentNode = default(T);

        public int RecurseTree(T rootNode, Action<T> processParent, Action<T> processChild, Func<T, int> getLeafCountFromNode, Func<T, int, T> getLeafFromNodeAtIndex)
        {
            parents = new List<T>();
            counters = new List<int>();

            var function = Trampoline.MakeTrampoline((T tn) =>
            {
                if (getLeafCountFromNode(tn) > 0)
                {
                    if (!parents.Contains(tn))  //if this is the first time this parent is being processed
                    {
                        parents.Add(tn);
                        counters.Add(getLeafCountFromNode(tn) - 1);

                        currentNode = getLeafFromNodeAtIndex(tn, (getLeafCountFromNode(tn) - 1));

                        processParent(tn);
                        pnc++;

                        return Trampoline.Recurse<T, int>(currentNode); //Recurse passing in child . . .
                    }
                    else
                    {
                        if (counters[pnc] >= 0)
                        {
                            currentNode = getLeafFromNodeAtIndex(parents[pnc], counters[pnc]);

                            return Trampoline.Recurse<T, int>(currentNode); //Recurse passing in child . . .
                        }
                        else
                        {
                            if (pnc == 0)
                            {
                                resetCounters();

                                return Trampoline.ReturnResult<T, int>(0);  //Return result . . .
                            }

                            parents.Remove(parents[pnc]);
                            counters.Remove(counters[pnc]);
                            pnc--;

                            //remember to mark this parent node as processed in its parent node's counter
                            counters[pnc] = counters[pnc] - 1;

                            //Recurse passing in parent . . .
                            return Trampoline.Recurse<T, int>(parents[pnc]);
                        }
                    }
                }
                else
                {
                    processChild(tn);

                    //mark as processed by subtracting one from the parent node's counter
                    counters[pnc] = counters[pnc] - 1;

                    //Recurse passing in parent . . .
                    return Trampoline.Recurse<T, int>(parents[pnc]);
                }
            });

            return function(rootNode);
        }

        private void resetCounters()
        {
            pnc = -1;
            parents = null;
            counters = null;
            currentNode = default(T);
        }

        #endregion

    }//end of class
}//end of namespace
